package org.wzy.springmvc.framework.servlet;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.wzy.springmvc.framework.annotation.MController;
import org.wzy.springmvc.framework.annotation.MRequestMapping;
import org.wzy.springmvc.framework.annotation.MRequestParam;
import org.wzy.springmvc.framework.context.MApplicationContext;

public class MDispatcherServlet extends HttpServlet {

	private static final String LOCATION = "contextConfigLocation";

	private List<MHandler> handlerMapping = new ArrayList<>();

	private Map<MHandler, MHandlerAdapter> handlerAdatpers = new HashMap<>();

	private List<MViewResolver> viewResolvers = new ArrayList<>();

	private static final long serialVersionUID = -5877571889790541653L;

	@Override
	public void init(ServletConfig config) throws ServletException {
		/**
		 * IOC容器必须要先初始化 假装容器已启动
		 */
		MApplicationContext context = new MApplicationContext(config.getInitParameter(LOCATION));

		// 请求解析
		initMultipartResolver(context);
		// 国际化
		initLocaleResolver(context);
		// 主题View层的
		initThemeResolver(context);

		// 解析url和Method的关系
		initHandlerMappings(context);
		// 适配器(匹配的过程)
		initHandlerAdapters(context);

		// 异常解析
		initHandlerExceptionResolvers(context);
		// 视图转发(根据视图名称匹配到一个具体模板)
		initRequestToViewNameTranslator(context);
		// 解析模板中的内容(拿到服务器传过来的数据，生成HTML代码)
		initViewResolvers(context);
		initFlashMapManager(context);

		System.out.println("MSpringMVC已经初始化......");
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	/**
	 * 在这里调用自己写的controller方法
	 */
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			doDispatch(request, response);
		} catch (Exception e) {
			response.getWriter().write("500 Exception... \n Message = " + Arrays.toString(e.getStackTrace()));
		}
	}

	private void initMultipartResolver(MApplicationContext context) {
	}

	private void initLocaleResolver(MApplicationContext context) {
	}

	private void initThemeResolver(MApplicationContext context) {
	}

	private void initHandlerMappings(MApplicationContext context) {
		Map<String, Object> ioc = context.getAll();
		if (ioc.isEmpty()) {
			return;
		}

		// 只要是由controller修饰的类，里面的方法全部找出来
		// 并且这个方法上应该要加RequestMapping注解，如果每加这个注解，则这个方法是不能被外界访问的
		for (Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			if (!clazz.isAnnotationPresent(MController.class)) {
				continue;
			}

			String url = "";
			if (clazz.isAnnotationPresent(MRequestMapping.class)) {
				MRequestMapping requestMapping = clazz.getAnnotation(MRequestMapping.class);
				url = requestMapping.value();
			}

			// 扫描controller下所有的方法
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (!method.isAnnotationPresent(MRequestMapping.class)) {
					continue;
				}

				// RequestMapping 会配置一个url，那么一个url就对应一个方法
				MRequestMapping requestMapping = method.getAnnotation(MRequestMapping.class);

				handlerMapping.add(new MHandler(Pattern.compile((url + requestMapping.value()).replaceAll("/+", "/")),
						entry.getValue(), method));
			}
		}
	}

	/**
	 * 初始化HandlerAdapter
	 * 
	 * @param context
	 */
	private void initHandlerAdapters(MApplicationContext context) {
		if (handlerMapping.isEmpty()) {
			return;
		}

		// 参数类型作为key，参数索引号作为值
		Map<String, Integer> paramMapping = new HashMap<>();
		for (MHandler handler : handlerMapping) {
			Class<?>[] parameterTypes = handler.method.getParameterTypes();

			// 有顺序，但是通过反射没法拿到我们参数的名字
			// 匹配自定义参数列表
			for (int i = 0; i < parameterTypes.length; i++) {
				Class<?> type = parameterTypes[i];
				if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
					paramMapping.put(type.getName(), i);
				}
			}

			// 匹配request和response
			Annotation[][] paramAnnos = handler.method.getParameterAnnotations();
			for (int i = 0; i < paramAnnos.length; i++) {
				for (Annotation annotation : paramAnnos[i]) {
					if (annotation instanceof MRequestParam) {
						String paramName = ((MRequestParam) annotation).value();
						if (!"".equals(paramName.trim())) {
							paramMapping.put(paramName, i);
						}
					}
				}
			}

			handlerAdatpers.put(handler, new MHandlerAdapter(paramMapping));
		}
	}

	private void initHandlerExceptionResolvers(MApplicationContext context) {
	}

	private void initRequestToViewNameTranslator(MApplicationContext context) {
	}

	private void initViewResolvers(MApplicationContext context) {
		// 模板一般是不会放在WebRoot下的，而是放在WEB-INF下，或classess下
		// 这样就避免了用户直接请求到模板
		// 加载模板个数，存储到缓存中
		// 检查模板中的语法错误
		String templateRoot = context.getProperties().getProperty("templateRoot");

		// 丫其实就一普通文件
		String rootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
		File rootFiles = new File(rootPath);
		for (File fileTmp : rootFiles.listFiles()) {
			viewResolvers.add(new MViewResolver(fileTmp.getName(), fileTmp));
		}
	}

	private void initFlashMapManager(MApplicationContext context) {
	}

	private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			// 先从HanlerMapping中取出一个handler
			MHandler handler = getMHandler(request);
			if (handler == null) {
				response.getWriter().write("404 Not Found...");
				return;
			}

			// 再取出一个适配器
			MHandlerAdapter ha = getMHandlerAdapter(handler);

			// 最后由适配器去调用具体实现
			MModelAndView mv = ha.handle(request, response, handler);

			// 写一个咕泡模板框架
			// Velocity #
			// Freemarker #
			// JSP ${name}
			// 自定义实现 @{name}
			applyDefaultViewName(response, mv);
		} catch (Exception e) {
			throw e;
		}
	}

	public void applyDefaultViewName(HttpServletResponse response, MModelAndView mv) throws Exception {
		if (mv == null) {
			return;
		}
		if (viewResolvers.isEmpty()) {
			return;
		}
		
		for (MViewResolver resolver : viewResolvers) {
			if (!mv.getView().equals(resolver.getViewName())) {
				continue;
			}
			
			String r = resolver.parse(mv);
			if (null != r) {
				response.setCharacterEncoding("UTF-8");
				response.getWriter().write(r);
			}
		}
	}

	private MHandler getMHandler(HttpServletRequest request) {
		// 循环 handlerMapping
		if (handlerMapping.isEmpty()) {
			return null;
		}

		// 匹配正则
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();

		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (MHandler handler : handlerMapping) {
			Matcher matcher = handler.pattern.matcher(url);
			if (!matcher.matches()) {
				continue;
			}

			return handler;
		}

		return null;
	}

	private MHandlerAdapter getMHandlerAdapter(MHandler handler) {
		if (handler == null)
			return null;

		return handlerAdatpers.get(handler);
	}

	/**
	 * HandlerMapping 定义
	 * 
	 * @author wzy_h
	 *
	 */
	private class MHandler {
		protected Object controller;
		protected Method method;
		protected Pattern pattern;

		protected MHandler(Pattern pattern, Object controller, Method method) {
			this.pattern = pattern;
			this.controller = controller;
			this.method = method;
		}
	}

	/**
	 * 方法适配器
	 * 
	 * @author wzy_h
	 *
	 */
	private class MHandlerAdapter {
		private Map<String, Integer> paramMapping;

		public MHandlerAdapter(Map<String, Integer> paramMapping) {
			this.paramMapping = paramMapping;
		}

		// 主要目的是用反射调用url对应的method
		public MModelAndView handle(HttpServletRequest request, HttpServletResponse response, MHandler handler)
				throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
			// 为什么要传request、response、handler?
			Class<?>[] parameterTypes = handler.method.getParameterTypes();

			// 要想给参数赋值，只能通过索引号来找到具体的某个参数

			Object[] paramValues = new Object[parameterTypes.length];

			@SuppressWarnings("unchecked")
			Map<String, String[]> parameterMap = request.getParameterMap();
			for (Entry<String, String[]> entry : parameterMap.entrySet()) {
				String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");

				if (!this.paramMapping.containsKey(entry.getKey())) {
					continue;
				}

				int index = this.paramMapping.get(entry.getKey());

				// 单个赋值是不行的
				paramValues[index] = castStringValue(value, parameterTypes[index]);
			}

			// request和response也需要赋值
			if (this.paramMapping.containsKey(HttpServletRequest.class.getName())) {
				int reqIndex = this.paramMapping.get(HttpServletRequest.class.getName());
				paramValues[reqIndex] = request;
			}
			if (this.paramMapping.containsKey(HttpServletResponse.class.getName())) {
				int respIndex = this.paramMapping.get(HttpServletResponse.class.getName());
				paramValues[respIndex] = response;
			}

			if (handler.method.getReturnType() == MModelAndView.class) {
				return (MModelAndView) handler.method.invoke(handler.controller, paramValues);
			}

			return null;
		}

		private Object castStringValue(String value, Class<?> clazz) {
			if (clazz == String.class) {
				return value;
			} else if (clazz == Integer.class) {
				return Integer.valueOf(value);
			} else if (clazz == int.class) {
				return Integer.valueOf(value).intValue();
			} else {
				return null;
			}
		}
	}

	/**
	 * 在Spring当中ViewResolver是一个接口(为了扩展性，各类模板可以自由实现其并定义自己的方法)
	 * 
	 * @author wzy_h
	 *
	 */
	private class MViewResolver {
		private String viewName;
		private File file;

		protected MViewResolver(String viewName, File file) {
			this.viewName = viewName;
			this.file = file;
		}

		protected String parse(MModelAndView mv) throws Exception {
			RandomAccessFile accessFile = new RandomAccessFile(this.file, "r");
			
			StringBuffer sb;
			try {
				sb = new StringBuffer();
				
				// 模板框架的语法是很复杂的，但是原理基本一致
				// 无非是利用正则表达式来处理字符串而已
				String line = null;
				while(null != (line = accessFile.readLine())) {
					Matcher matcher = matcher(line);
					while (matcher.find()) {
						for (int i = 1; i <= matcher.groupCount(); i++) {
							String param = matcher.group(i);
							Object paramValue = mv.getModel().get(param);
							if (null == paramValue) {
								continue;
							}
							
							line = line.replaceAll("@\\{" + param + "\\}", paramValue.toString());
						}
					}
					
					sb.append(line);
				}
			} finally {
				accessFile.close();
			}
			return sb.toString();
		}
		
		private Matcher matcher(String str) {
			Pattern p = Pattern.compile("@\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
			return p.matcher(str);
		}

		public String getViewName() {
			return viewName;
		}
	}

	public List<MViewResolver> getViewResolvers() {
		return viewResolvers;
	}
}
